/* Copyright 2015 the SumatraPDF project authors (see AUTHORS file).
License: Simplified BSD (see COPYING.BSD) */

#include "BaseUtil.h"
#include "WinDynCalls.h"
#include "WinUtil.h"

#define API_DECLARATION(name)                                                                      \
    \
Sig_##name Dyn##name = nullptr;

KERNEL32_API_LIST(API_DECLARATION)
NTDLL_API_LIST(API_DECLARATION)
UXTHEME_API_LIST(API_DECLARATION)
NORMALIZ_API_LIST(API_DECLARATION)
KTMW32_API_LIST(API_DECLARATION)
USER32_API_LIST(API_DECLARATION)
DWMAPI_API_LIST(API_DECLARATION)
UIA_API_LIST(API_DECLARATION)
DBGHELP_API_LIST(API_DECLARATION)

#undef API_DECLARATION

#define API_LOAD(name) Dyn##name = (Sig_##name)GetProcAddress(h, #name);

// Loads a DLL explicitly from the system's library collection
HMODULE SafeLoadLibrary(const WCHAR *dllName) {
    WCHAR dllPath[MAX_PATH];
    UINT res = GetSystemDirectoryW(dllPath, dimof(dllPath));
    if (!res || res >= dimof(dllPath))
        return nullptr;
    BOOL ok = PathAppendW(dllPath, dllName);
    if (!ok)
        return nullptr;
    return LoadLibraryW(dllPath);
}

void InitDynCalls() {
    HMODULE h = SafeLoadLibrary(L"kernel32.dll");
    CrashAlwaysIf(!h);
    KERNEL32_API_LIST(API_LOAD);

    h = SafeLoadLibrary(L"ntdll.dll");
    CrashAlwaysIf(!h);
    NTDLL_API_LIST(API_LOAD);

    h = SafeLoadLibrary(L"user32.dll");
    CrashAlwaysIf(!h);
    USER32_API_LIST(API_LOAD);

    h = SafeLoadLibrary(L"uxtheme.dll");
    if (h) {
        UXTHEME_API_LIST(API_LOAD);
    }

    h = SafeLoadLibrary(L"dwmapi.dll");
    if (h) {
        DWMAPI_API_LIST(API_LOAD);
    }

    h = SafeLoadLibrary(L"normaliz.dll");
    if (h) {
        NORMALIZ_API_LIST(API_LOAD);
    }

    h = SafeLoadLibrary(L"ktmw32.dll");
    if (h) {
        KTMW32_API_LIST(API_LOAD);
    }

    h = SafeLoadLibrary(L"uiautomationcore.dll");
    if (h) {
        UIA_API_LIST(API_LOAD)
    }

#if 0
    WCHAR *dbghelpPath = L"C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Team Tools\\Performance Tools\\dbghelp.dll";
    h = LoadLibrary(dbghelpPath);
#else
    h = SafeLoadLibrary(L"dbghelp.dll");
#endif
    if (h) {
        DBGHELP_API_LIST(API_LOAD)
    }
}

#undef API_LOAD

namespace touch {

bool SupportsGestures() { return DynGetGestureInfo && DynCloseGestureInfoHandle; }

BOOL GetGestureInfo(HGESTUREINFO hGestureInfo, PGESTUREINFO pGestureInfo) {
    if (!DynGetGestureInfo)
        return FALSE;
    return DynGetGestureInfo(hGestureInfo, pGestureInfo);
}

BOOL CloseGestureInfoHandle(HGESTUREINFO hGestureInfo) {
    if (!DynCloseGestureInfoHandle)
        return FALSE;
    return DynCloseGestureInfoHandle(hGestureInfo);
}

BOOL SetGestureConfig(HWND hwnd, DWORD dwReserved, UINT cIDs, PGESTURECONFIG pGestureConfig,
                      UINT cbSize) {
    if (!DynSetGestureConfig)
        return FALSE;
    return DynSetGestureConfig(hwnd, dwReserved, cIDs, pGestureConfig, cbSize);
}
}

namespace theme {

bool IsAppThemed() {
    if (DynIsAppThemed && DynIsAppThemed()) {
        return true;
    }
    return false;
}

HTHEME OpenThemeData(HWND hwnd, LPCWSTR pszClassList) {
    if (DynOpenThemeData) {
        return DynOpenThemeData(hwnd, pszClassList);
    }
    return nullptr;
}

HRESULT CloseThemeData(HTHEME hTheme) {
    if (DynCloseThemeData) {
        return DynCloseThemeData(hTheme);
    }
    return E_NOTIMPL;
}

HRESULT DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT pRect,
                            LPCRECT pClipRect) {
    if (DynDrawThemeBackground) {
        return DynDrawThemeBackground(hTheme, hdc, iPartId, iStateId, pRect, pClipRect);
    }
    return E_NOTIMPL;
}

BOOL IsThemeActive() {
    if (DynIsThemeActive) {
        return DynIsThemeActive();
    }
    return FALSE;
}

BOOL IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId, int iStateId) {
    if (DynIsThemeBackgroundPartiallyTransparent) {
        return DynIsThemeBackgroundPartiallyTransparent(hTheme, iPartId, iStateId);
    }
    return FALSE;
}

HRESULT GetThemeColor(HTHEME hTheme, int iPartId, int iStateId, int iPropId, COLORREF *pColor) {
    if (DynGetThemeColor) {
        return DynGetThemeColor(hTheme, iPartId, iStateId, iPropId, pColor);
    }
    return E_NOTIMPL;
}
};

namespace dwm {

BOOL IsCompositionEnabled() {
    if (!DynDwmIsCompositionEnabled)
        return FALSE;
    BOOL isEnabled;
    if (SUCCEEDED(DynDwmIsCompositionEnabled(&isEnabled)))
        return isEnabled;
    return FALSE;
}

HRESULT ExtendFrameIntoClientArea(HWND hwnd, const MARGINS *pMarInset) {
    if (!DynDwmExtendFrameIntoClientArea)
        return E_NOTIMPL;
    return DynDwmExtendFrameIntoClientArea(hwnd, pMarInset);
}

BOOL DefWindowProc_(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) {
    if (!DynDwmDefWindowProc)
        return FALSE;
    return DynDwmDefWindowProc(hwnd, msg, wParam, lParam, plResult);
}

HRESULT GetWindowAttribute(HWND hwnd, DWORD dwAttribute, void *pvAttribute, DWORD cbAttribute) {
    if (!DynDwmGetWindowAttribute)
        return E_NOTIMPL;
    return DynDwmGetWindowAttribute(hwnd, dwAttribute, pvAttribute, cbAttribute);
}
};

namespace uia {

LRESULT ReturnRawElementProvider(HWND hwnd, WPARAM wParam, LPARAM lParam,
                                 IRawElementProviderSimple *provider) {
    if (!DynUiaReturnRawElementProvider)
        return 0;
    return DynUiaReturnRawElementProvider(hwnd, wParam, lParam, provider);
}

HRESULT HostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **pProvider) {
    if (!DynUiaHostProviderFromHwnd)
        return E_NOTIMPL;
    return DynUiaHostProviderFromHwnd(hwnd, pProvider);
}

HRESULT RaiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id) {
    if (!DynUiaRaiseAutomationEvent)
        return E_NOTIMPL;
    return DynUiaRaiseAutomationEvent(pProvider, id);
}

HRESULT RaiseStructureChangedEvent(IRawElementProviderSimple *pProvider,
                                   StructureChangeType structureChangeType, int *pRuntimeId,
                                   int cRuntimeIdLen) {
    if (!DynUiaRaiseStructureChangedEvent)
        return E_NOTIMPL;
    return DynUiaRaiseStructureChangedEvent(pProvider, structureChangeType, pRuntimeId,
                                            cRuntimeIdLen);
}

HRESULT GetReservedNotSupportedValue(IUnknown **punkNotSupportedValue) {
    if (!DynUiaRaiseStructureChangedEvent)
        return E_NOTIMPL;
    return DynUiaGetReservedNotSupportedValue(punkNotSupportedValue);
}
};

static const WCHAR *dllsToPreload = L"comctl32.dll\0gdiplus.dll\0msimg32.dll\0shlwapi.dll\0urlmon.dll\0version.dll\0windowscodecs.dll\0wininet.dll\0\0";

// try to mitigate dll hijacking by pre-loading all the dlls that we delay load or might
// be loaded indirectly
void NoDllHijacking() {
    const WCHAR *dll = dllsToPreload;
    while (*dll) {
        SafeLoadLibrary(dll);
        seqstrings::SkipStr(dll);
    }
}

#pragma warning(push)
//  C4201: nonstandard extension used: nameless struct/union
#pragma warning(disable : 4201)
// https://msdn.microsoft.com/en-us/library/windows/desktop/mt706245(v=vs.85).aspx
typedef struct _PROCESS_MITIGATION_IMAGE_LOAD_POLICY {
    union {
        DWORD  Flags;
        struct {
            DWORD NoRemoteImages : 1;
            DWORD NoLowMandatoryLabelImages : 1;
            DWORD PreferSystem32Images : 1;
            DWORD ReservedFlags : 29;
        };
    };
} PROCESS_MITIGATION_IMAGE_LOAD_POLICY, *PPROCESS_MITIGATION_IMAGE_LOAD_POLICY;
#pragma warning(pop)

// https://github.com/videolan/vlc/blob/8663561d3f71595ebf116f17279a495b67cac713/bin/winvlc.c#L84
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh769088(v=vs.85).aspx
// Note: on win 10 it doesn't seem to prevent loading bad Version.dll from the same directory
// as sumatra. Is it because it's loaded before I get a chance to call this function?
// Am I calling it wrong?
// What happens for other dlls?
void PrioritizeSystemDirectoriesForDllLoad() {
    if (!DynSetProcessMitigationPolicy) {
        return;
    }
    // Only supported since Win 10
    PROCESS_MITIGATION_IMAGE_LOAD_POLICY m = { 0 };
    m.PreferSystem32Images = 1;
    DynSetProcessMitigationPolicy(ProcessImageLoadPolicy, &m, sizeof(m));
    DbgOutLastError();
}
